/*
 ------------------------------------------------------------------------------
	sdlmedia - a test program for a media search library, including test UI.

	Copyright (C) 2011 Pierre Veber
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 3 of the License, or
	(at your option) any later version.
	
	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software Foundation,
	Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
------------------------------------------------------------------------------
*/

#include "common.h"
#include "thread.h"
#include "fastheap.h"
#include "sdlmedia.h"
#include "medialib.h"
#include "id3.h"

#define THIS_FILTER	TRACE_LIB

/*
 ------------------------------------------------------------------------------
	FAST HEAPs (see fastheap.h)
	used to store pathes, filenames, tags of media records.
------------------------------------------------------------------------------
*/

#define MEDIA_HEAP_PAGE_SIZE	262144
#define FILE_HEAP_PAGE_SIZE		262144
#define DIR_HEAP_PAGE_SIZE		524288
#define TAG_HEAP_PAGE_SIZE		524288

static FastHeap_t* pDirHeap = NULL;
static FastHeap_t* pFileHeap = NULL;
static FastHeap_t* pMediaHeap = NULL;
static FastHeap_t* pTagHeap = NULL;

/*
 ------------------------------------------------------------------------------
	List of decoders.
	Stores supported extensions and functions for playing media.
------------------------------------------------------------------------------
*/

MediaDecoder_t Decoders[] = {
	{ 
		"Mad",
		"mp3",
		_id3_get_info,
		_id3_get_album_picture,
		madDecode,
		madStop,
		madSeek,
		madPause,
		madResume
	} 
};

/*
 ------------------------------------------------------------------------------
	MediaLib_t* pMediaLib
	Global head for media library list.
------------------------------------------------------------------------------
*/

static MediaLib_t* pMediaLib = NULL;

/*
 ------------------------------------------------------------------------------
	MediaFile_t basic manipulation routines.

		_media_file_new:
			allocation on Media Heap. Memsets to 0.

------------------------------------------------------------------------------
*/

MediaFile_t* _media_file_new()
{
	MediaFile_t* pOut = (MediaFile_t*)_fast_heap_alloc(pMediaHeap, sizeof(MediaFile_t));
	if (pOut != NULL)
	{
		memset(pOut, 0, sizeof(MediaFile_t));
	}
	return pOut;
}

/*
 ------------------------------------------------------------------------------
	MediaLib_t basic manipulation routines.

		_media_lib_new:
			Standard allocation, copies (strdup) path in structure.
			Allocates Dir and File Heaps.
		_media_lib_free:
			Frees members and node.
			Does not deallocate Dir and File Heaps.
------------------------------------------------------------------------------
*/

MediaLib_t* _media_lib_new(const char* pszPath)
{
	MediaLib_t* pOut = (MediaLib_t*)malloc(sizeof(MediaLib_t));
	if (pOut != NULL)
	{
		pOut->pHead = NULL;
		pOut->pTail = NULL;
		pOut->pszPath = _strdup(pszPath);
		pOut->pNext = NULL;
		pOut->nFiles = 0;
	}
	if (pMediaLib == NULL)
	{
		pMediaLib = pOut;
		pDirHeap = _fast_heap_new(DIR_HEAP_PAGE_SIZE);
		pFileHeap = _fast_heap_new(FILE_HEAP_PAGE_SIZE);
	}
	else
	{
		MediaLib_t* pLast = pMediaLib;
		while (pLast->pNext != NULL)
		{
			pLast = pLast->pNext;
		}
		pLast->pNext = pOut;
	}
	return pOut;
}

void _media_lib_free(MediaLib_t* pLib)
{
	if (pLib != NULL)
	{
		if (pLib->pszPath != NULL)
		{
			free(pLib->pszPath);
		}
		// free media files
	}

	// free heaps
	_fast_heap_free(pDirHeap);
	_fast_heap_free(pFileHeap);
}

/*
 ------------------------------------------------------------------------------
	MediaDecoder_t basic manipulation routines.

		_get_decoder:
			Checks whether filename passed in parameters is handled by one
			decoder. Returns the index of found decoder in global Decoders 
			array, (-1) if not found.
------------------------------------------------------------------------------
*/

static int _get_decoder(char* pszFileName)
{
	char* pExt = strrchr(pszFileName, '.');
	
	for (	
			int i=0;
			(i < sizeof(Decoders)/sizeof(MediaDecoder_t) && (pExt != NULL)); 
			i++
		)
	{
		char* pSup = Decoders[i].szSupportedExtensions;
		char* p = ++pExt;
		
		while ( (*pSup != '\0') &&  (*pSup != '|') )
		{
			if ( tolower(*pSup) != tolower(*p) )
			{
				pSup = strchr(pSup, '|');
				if (pSup == NULL)
				{
					return -1;
				}
				else
				{
					pSup++;
					p = pExt;
					continue;
				}
			}
			pSup++;
			p++;
		}
		if ( p != pExt )
		{
			return i;
		}
	}
	return -1;
}

/*
 ------------------------------------------------------------------------------
	Media Library File Search

		_media_lib_fill:
			Entry point. Creates thread and passes pointer to MediaLib_t.
			Thread procedure is _media_fill_proc.

		_media_fill_proc:
			Executed in a separate thread. Walks through the Media Library 
			folder, and for each file found, calls _on_file_found handler.
			After the walk is finished, signals that Media Library dir mode 
			is accessible. Then starts media file tag reading by calling 
			_media_tag_fill.

		_media_tag_fill:
			To do...
------------------------------------------------------------------------------
*/

static int _media_tag_fill(MediaLib_t* pLib)
{
	DWORD dwStart, dwEnd;
	SDL_Event event;
	MediaFile_t* pFile = pLib->pHead;
	char* pszFullPath = NULL;

	if (pTagHeap == NULL)
	{
		pTagHeap = _fast_heap_new(TAG_HEAP_PAGE_SIZE);
	}

	dwStart = GetTickCount();
	while (pFile != NULL)
	{
		pFile->pDecoder->fpMediaGetInfo(pTagHeap, pFile->pszFullPath, &(pFile->info));
		pFile = pFile->pNext;
	}
	dwEnd = GetTickCount();
	
	TRACE("_media_tag_fill - %d files processed in %d milliseconds.\n", pLib->nFiles, (int)(dwEnd-dwStart));

	/* signal that library is accessible in dir mode */
	event.type = SDL_USEREVENT;
	event.user.code = EV_MEDIA_LIB_TAGS_READY;
	event.user.data1 = pLib;
	event.user.data2 = 0;

	SDL_PushEvent(&event);

	return 0;
}


static int _on_file_found(void* pData, char* pszBase, char* pszFileName)
{
	MediaLib_t* pLib = (MediaLib_t*)pData;
	int dec = _get_decoder(pszFileName);

	if (dec != -1)
	{
		if (pMediaHeap == NULL)
		{
			pMediaHeap = _fast_heap_new(MEDIA_HEAP_PAGE_SIZE);
		}
		MediaFile_t* pFile = _media_file_new();
		if (pFile != NULL)
		{
			pFile->pDecoder = &Decoders[dec];
			pFile->pszFullPath = (char*)_fast_heap_dup(pFileHeap, pszFileName, strlen(pszBase)+strlen(pszFileName)+2);
			if (pFile->pszFullPath != NULL)
			{
				sprintf(pFile->pszFullPath, "%s\\%s", pszBase, pszFileName);
			}
			if (pLib->pTail != NULL)
			{
				pLib->pTail->pNext = pFile;
				pLib->pTail = pFile;
			}
			else
			{
				pLib->pHead = pLib->pTail = pFile;
			}
			pLib->nFiles++;
		}
	}
	return 0;
}

static int _media_fill_proc(Handle_t* pHandle, void* pData)
{
	DWORD dwStart, dwEnd;
	MediaLib_t* pLib = (MediaLib_t*)pData;
	SDL_Event event;

	dwStart = GetTickCount();
	_walk(pDirHeap, pLib->pszPath, _on_file_found, pLib);
	dwEnd = GetTickCount();
	
	TRACE("_media_fill_proc - %d files found in %d milliseconds.\n", pLib->nFiles, (int)(dwEnd-dwStart));

	/* signal that library is accessible in dir mode */
	event.type = SDL_USEREVENT;
	event.user.code = EV_MEDIA_LIB_FOLDERS_READY;
	event.user.data1 = pLib;
	event.user.data2 = 0;

	SDL_PushEvent(&event);

	/* starts filling tag information 
	 commented for testing*/ 
	_media_tag_fill(pLib);

	return pLib->nFiles;
}

int _media_lib_fill(MediaLib_t* pLib)
{
	Handle_t* pThread = CreateThread(_media_fill_proc, pLib);
	if (pThread == NULL)
	{
		TRACE("Unable to create media fill thread.\n");
		return 1;
	}
	return 0;
}

MediaFile_t* _media_lib_get_from_path(MediaLib_t* pLib, const char* pszFilePath)
{
	MediaFile_t* pFile = pLib->pHead;
	while (pFile != NULL)
	{
		if (strcmp(pFile->pszFullPath, pszFilePath) == 0)
		{
			break;
		}
		pFile = pFile->pNext;
	}
	return pFile;
}
